from decorator import decorator
import random

class Location:
    def __init__(self,manager,pos):
        self.manager=manager
        self.pos=pos

    def name(self,player):
        return self.__class__.__name__

    def profile(self):
        return "Pos :{}\nName:{}".format(self.pos,self.name(None))

class Void(Location):
    def run(self,player):
        pass

    def action(self):
        pass

class Start(Void):
    pass

class News(Location):
    def __init__(self,*args,**kwargs):
        Location.__init__(self,*args,**kwargs)

        self.newsTitle=self.manager.config.news.newsTitle

    def run(self,player):
        self.action(player)

    def action(self,player):
        choice=random.choice([self.bonus,self.charity,self.interest,self.tax,self.cards])
        choice()

    @decorator
    def showNews(func,self,*args,**kwargs):
        title=random.choice(self.newsTitle)
        self.manager.game.showText(title+' '+func(self,*args,**kwargs))

    @showNews
    def bonus(self):
        firstPlayer=sorted(self.manager.game.players)[-1]
        amount=random.randint(*self.manager.config.data.bonus)
        firstPlayer.cash+=amount
        return "Bonus the first land-owner {} for {}.".format(firstPlayer,amount)

    @showNews
    def charity(self):
        lastPlayer=sorted(self.manager.game.players)[0]
        amount=random.randint(*self.manager.config.data.charity)
        lastPlayer.cash+=amount
        return "Aid the last land-owner {} for {}.".format(lastPlayer,amount)

    @showNews
    def interest(self):
        for player in self.manager.game.players:
            player.saving+=int(player.saving*0.1)

        return "Every player get 10% interest of the savings."

    @showNews
    def tax(self):
        for player in self.manager.game.players:
            player.saving-=int(player.saving*0.1)

        return "Every player get 10% interest of the savings."

    @showNews
    def cards(self):
        for player in self.manager.game.players:
            card=self.manager.randomCard()
            player.inv[card]+=1

        return "Every player get a card as a gift (from Santa Claus, maybe)."

class Store(Location):

    def __init__(self,*args,**kwargs):
        Location.__init__(self,*args,**kwargs)

        self.itemList=[ (code,self.manager.game.cardList[code],price) for code,price in self.manager.config.cardPrice ]
        self.choiceList=[ 
                ('{} - {}pts.'.format(card.__name__,price),(code,price)) 
                for code,card,price in self.itemList 
                if card is not None 
                ]

    def run(self,player):
        while True:
            choice=self.manager.game.choice(self.choiceList)

            if choice==-1:
                break

            self.action(player,choice)

    def action(self,player,choice):
        if player.points<choice[1]:
            self.manager.game.error("Points not enough.")

        player.points-=choice[1]
        player.inv[choice[0]]+=1

class Bank(Location):
    def __init__(self,*args,**kwargs):
        Location.__init__(self,*args,**kwargs)

        self.manager.regPassby(self.pos,'Bank',self.run)

    def run(self,player):
        check=lambda x: not x or ( int(x)+player.saving>=0 and player.cash-int(x)>=0 )

        while True:
            amount = self.manager.game.getInput(
                    prompt="Account:{}.\nSaving changes: positive as save and negtive as deposit, return to exit.".format(player.saving),
                    errorMsg="The amount you input is either too large, or not an integer. Please try again.",
                    check=check,
                    )

            if not amount:
                break

            self.action(player,int(amount))

    def action(self,player,amount):
        player.saving+=amount
        player.cash-=amount

class Lottery(Location):
    def __init__(self,*args,**kwargs):
        Location.__init__(self,*args,**kwargs)
        
        self.choiceList=[
                ("Show the pool.",self.manager.showPool),
                ("Buy lottery, of course.", self.main),
                ]

        self.cost=self.manager.config.data.lotteryCost
    
    def run(self,player):
        while True:
            choice=self.manager.game.choice(self.choiceList)

            if choice==-1:
                break
            
            choice(player)

    def main(self,player):
        check=lambda x: not x or ( int(x) not in self.manager.lotteryPool and 1<=int(x)<=20 )

        while True:
            if player.cash < self.cost:
                self.manager.game.showText("Cash not enough to buy a signle poll.")
                break

            poll = self.manager.game.getInput(
                    prompt="Buy lottery with number X (1<=x<=20), return to exit.",
                    errorMsg="The number you input is either taken or is not in range.",
                    )
            
            if not poll:
                break

            self.action(player,int(poll))

    def action(self,player,poll):
        player.cash-=self.cost
        self.manager.lotteryPool[poll]=player
        self.manager.lotterySum+=self.cost

class ItemForFree(Location):
    def run(self,player):
        card=self.manager.randomCard()

        self.action(player,card)

    def action(self,player,card):
        player.inv[card]+=1

        self.manager.game.showText("You get a card {} for free!".format(self.manager.game.cardList[card]))

class PointsForFree(Location):
    def run(self,player):
        amount=random.randint(*self.manager.config.data.pointsFree)

        self.action(player,amount)

    def action(self,player,amount):
        player.points+=amount
        self.manager.game.showText("You get {}pts for free!".format(amount))

class Estate(Location):
    def __init__(self,manager,pos,level=1,street=None,owner=None):
        Location.__init__(self,manager,pos)
        self.level=level
        self.street=street
        self.owner=owner

        self.cost=int(round(random.randint(*self.manager.config.data.estatePrice)/10.0)*10)

    def value(self):
        return self.cost*self.level

    def __cmp__(self,other):
        return cmp(self.value(),other.value()) or cmp(id(self),id(other))

    def run(self,player):
        if self.owner is None:
            self.runNoneOwner(player)
        elif self.owner==player:
            self.runSelfOwner(player)
        else:
            self.runOtherOwner(player)

    def runOtherOwner(self,player):
        streetBonus=sum(estate.value() for estate in self.manager.gameMap.streets[self.street])*0.1
        amount=int(self.value()*0.3+streetBonus)

        self.actionOtherOwner(player,amount)

    def actionOtherOwner(self,player,amount):
        self.manager.game.showText("You have to pay {} to {}.".format(amount,self.owner))

        if player.cash>=amount:
            player.cash-=amount
            self.owner.cash+=amount

            return

        amount-=player.cash
        self.owner.cash+=player.cash
        player.cash=0
        self.manager.game.showText("Your cash is not enough, automated using saving.")

        if player.saving>=amount:
            player.saving-=amount
            self.owner.cash+=amount

            self.manager.game.showText("Cost saving {}.".format(amount))
            return
        
        self.manager.game.showText("Your saving is not enough, automated selling estates.")

        player.estates.sort(reverse=True)
        while player.estates:
            estate=player.estates.pop(0)
            self.manager.game.showText("Selling estate at postion {}.".format(estate.pos))
            evalue=estate.value()

            if evalue>=ramount:
                self.owner.cash+=amount
                player.cash+=evalue-amount
                self.manager.game.showText("After paying, your cash adds for {}.".format(evalue-amount))
                return
            else:
                amount-=evalue

        self.game.showText("Pity. All your estates adds up does not makes up your credit. Losing.")
        self.manager.lose(player)

    def runSelfOwner(self,player):
        self.manager.game.showText("The land beneath you is yours.")
        check=lambda x: x in ["Y","N"]

        while True:
            if self.level==self.manager.config.data.levelMax:
                return

            cost=int(self.value()*0.3)
            if player.cash<cost:
                self.manager.game.showText("Not enough cash to upgrade.")
                return

            choice=self.manager.game.getInput(
                    prompt="Upgrade the estate from level {} to {} by paying {}?[Y/N]".format(self.level,self.level+1,cost),
                    check=check,
                    )
            
            if choice=="N":
                return

            self.actionSelfOwner(player,cost)

    def actionSelfOwner(self,player,cost):
        player.cash-=cost
        self.level+=1

        self.manager.game.showText("Upgrade done.")

    def runNoneOwner(self,player):
        check=lambda x: x in ["Y","N"]

        cost=self.value()
        if cost>player.cash:
            self.manager.game.showText("Not enough cash to buy the estate.")
            return

        prompt="An estate with no owner of level {} found! Buy it for {}?[Y/N]".format(self.level,cost)


        choice=self.manager.game.getInput(
                prompt=prompt,
                check=check,
                )

        if choice=="N":
            return
        
        self.actionNoneOwner(player,cost)
        self.runSelfOwner(player)

    def actionNoneOwner(self,player,cost):
        player.cash-=cost
        self.owner=player
        player.estates.append(self)
        
        self.manager.game.showText("You get the estate!")

    def name(self,player):
        if self.owner is None:
            return "NoneOwnerEstate"
        elif self.owner == player:
            return "SelfOwnerEstate"
        else:
            return "OtherOwnerEstate"

    def profile(self):
        return "Pos :{}\nName:{}\nOwned By:{}\nLevel:{}\nValue:{}\n".format(
                self.pos,
                "Estate",
                self.owner,
                self.level,
                self.value(),
            )
